/*
 * anima/x86_64/cpu.hh - x86-64-specific CPU interface.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#ifndef HEADER_ANIMA_X86_64_CPU_HH
#define HEADER_ANIMA_X86_64_CPU_HH

#include HEADER (anima,cmdline.hh)
#include HEADER (x86_64,lapic.hh)
#include HEADER (x86_64,ioapic.hh)
#include HEADER (x86_64,mmu.hh)
#include HEADER (x86_64,descs.hh)
#include HEADER (x86_64,ints.hh)
#include HEADER (x86_64,tss.hh)
#include HEADER (utils,memory.hh)
#include HEADER (LIBBST_PREFIX,parent_node.hh)
#include HEADER (LIBBST_PREFIX,avl_tree.hh)

START_NAMESPACE (anima)

struct cpu_t;

END_NAMESPACE

START_NAMESPACE2 (anima,x86_64)

struct fwd_cpu_t
{
public:
  struct gprs_t
  {
    u64_t rax;
    u64_t rbx;
    u64_t rcx;
    u64_t rdx;
    u64_t rbp;
    u64_t rsi;
    u64_t rdi;
    u64_t r8;
    u64_t r9;
    u64_t r10;
    u64_t r11;
    u64_t r12;
    u64_t r13;
    u64_t r14;
    u64_t r15;

    INLINE
    gprs_t &
    operator= (const gprs_t &gprs)
    {
      rax = gprs.rax;
      rbx = gprs.rbx;
      rcx = gprs.rcx;
      rdx = gprs.rdx;
      rbp = gprs.rbp;
      rsi = gprs.rsi;
      rdi = gprs.rdi;
      r8 = gprs.r8;
      r9 = gprs.r9;
      r10 = gprs.r10;
      r11 = gprs.r11;
      r12 = gprs.r12;
      r13 = gprs.r13;
      r14 = gprs.r14;
      r15 = gprs.r15;

      return *this;
    }
  };

  struct ctx_t;

  struct int_frame_t : gprs_t
  {
    ::x86_64::int_frame_t hw_frame;

    int_frame_t &operator= (const ctx_t &ctx);
  };

  struct sc_frame_t : gprs_t
  {
    ::x86_64::flags_t flags;
    u64_t rip;
    u64_t rsp;
  };

  struct ctx_t : gprs_t
  {
    ::x86_64::flags_t flags;
    u8_t cs;
    u8_t ss;
    u64_t rip;
    u64_t rsp;

    ctx_t &operator= (const int_frame_t &frame);
  };
};

INLINE
fwd_cpu_t::int_frame_t &
fwd_cpu_t::int_frame_t::operator= (const ctx_t &ctx)
{
  *(gprs_t *) this = ctx;
  hw_frame.rip = ctx.rip;
  hw_frame.cs = ctx.cs;
  hw_frame.flags = ctx.flags;
  hw_frame.rsp = ctx.rsp;
  hw_frame.ss = ctx.ss;

  return *this;
}

INLINE
fwd_cpu_t::ctx_t &
fwd_cpu_t::ctx_t::operator= (const int_frame_t &frame)
{
  *(gprs_t *) this = frame;
  flags = frame.hw_frame.flags;
  cs = frame.hw_frame.cs;
  ss = frame.hw_frame.ss;
  rip = frame.hw_frame.rip;
  rsp = frame.hw_frame.rsp;

  return *this;
}

#define __ANIMA_X86_64_EXTERN_EVENT_HANDLER(NAME) \
  extern "C" void anima_x86_64_int_ ## NAME ## _entry (); \
  extern "C" void anima_x86_64_int_ ## NAME ## _handler \
    (fwd_cpu_t::int_frame_t *);

__ANIMA_X86_64_EXTERN_EVENT_HANDLER (ignore)

__ANIMA_X86_64_EXTERN_EVENT_HANDLER (apic_timer0)
__ANIMA_X86_64_EXTERN_EVENT_HANDLER (apic_timer)

#undef __ANIMA_X86_64_EXTERN_EVENT_HANDLER

struct cpu_t : public fwd_cpu_t
{
  friend class ::anima::cmdline;

public:
  typedef u8_t id_t;
  typedef void (*sc_handler_t) (sc_frame_t *frame);

protected:
  struct gdt_t
  {
    ::x86_64::desc_t null;
    ::x86_64::desc_t kernel_code;
    ::x86_64::desc_t user_code;
    ::x86_64::desc_t kernel_data;
    ::x86_64::desc_t user_data;
    ::x86_64::sys_desc_t tss;
  };

  struct ioapic_t : public ::x86_64::ioapic_t
  {
    friend class ::anima::cpu_t;
    friend class cpu_t;

  protected:
    u64_t addr;
    u32_t ints_base;
    u8_t _ints_count;

  protected:
    LIBBST_NS::basic_avl_node_t<LIBBST_NS::basic_parent_next_node_t> node;

  public:
    INLINE
    ioapic_t (void *mem, u64_t addr, u32_t ints_base)
      : ::x86_64::ioapic_t (mem)
    {
      this->addr = addr;
      this->ints_base = ints_base;
      _ints_count = get_version ().MRE;
    }

  public:
    INLINE
    u64_t
    get_addr () const
    {
      return addr;
    }

    INLINE
    u32_t
    get_ints_base () const
    {
      return ints_base;
    }

    INLINE
    u8_t
    ints_count () const
    {
      return _ints_count;
    }

  public:
    INLINE
    bool
    operator== (const ioapic_t &ioapic) const
    {
      return ioapic.ints_base + ioapic._ints_count > ints_base
             && ints_base + _ints_count > ioapic.ints_base;
    }

    INLINE
    bool
    operator< (const ioapic_t &ioapic) const
    {
      return ints_base + _ints_count <= ioapic.ints_base;
    }

    INLINE
    bool
    operator== (u32_t interrupt) const
    {
      return interrupt >= ints_base && interrupt < ints_base + _ints_count;
    }

    INLINE
    bool
    operator< (u32_t interrupt) const
    {
      return ints_base + _ints_count <= interrupt;
    }
  };

protected:
  static const u64_t CPU = -MB (2);
  static const u64_t PML4 = -MB (2) + KB (4);
  static const u64_t PDP = -MB (2) + KB (8);
  static const u64_t PD = -MB (2) + KB (12);
  static const u64_t PT = -MB (2) + KB (16);
  static const u64_t TEMP = -KB (52);
  static const u64_t LAPIC_BASE = -KB (48);
  static const u64_t SC_STACK = -KB (40);
  static const u64_t NMI_STACK = -KB (32);
  static const u64_t INT_STACK = -KB (24);
  static const u64_t TSS = -KB (16);

protected:
  typedef ::x86_64::lapic<LAPIC_BASE> lapic;

public:
  static const u16_t SELECTOR_NULL = offsetof (gdt_t, null);
  static const u16_t SELECTOR_KERNEL_CODE = offsetof (gdt_t, kernel_code);
  static const u16_t SELECTOR_USER_CODE = offsetof (gdt_t, user_code) | 3;
  static const u16_t SELECTOR_KERNEL_DATA = offsetof (gdt_t, kernel_data);
  static const u16_t SELECTOR_USER_DATA = offsetof (gdt_t, user_data) | 3;
  static const u16_t SELECTOR_TSS = offsetof (gdt_t, tss);

public:
  static const u8_t INT_APIC_TIMER = 0x20;
  static const u8_t INT_APIC_SI = 0x2F;

private:
  static const cmdline::option_t use_hlt_option;

protected:
  static gdt_t gdt;
  static ::x86_64::sys_desc_t idt[256];
  static sc_handler_t sc_handlers[256];
  static bool use_hlt_p;

protected:
  static u64_t ap_boot_code_paddr;
  static u64_t kernel_pdp_paddr;
  static u64_t tss_paddr;
  static u64_t tss_end_paddr;

protected:
  static volatile u32_t *lapic_mem;
  static u64_t apic_timer_freq;
  static u32_t apic_timer_clk_upd_tics;

protected:
  static LIBBST_NS::avl_tree_t<
           LIBBST_NS::parent_next_node_t<
             ioapic_t, LIBBST_NS::basic_avl_node_t<
                         LIBBST_NS::basic_parent_next_node_t>,
             offsetof (ioapic_t, node)>,
           LIBBST_NS::basic_min_tree_t, LIBBST_NS::min_glue_t> ioapics;

protected:
  u64_t pml4_paddr;

private:
  static void sc_wrapper ();

protected:
  static void sc_schedule (sc_frame_t *frame);

protected:
#define __ANIMA_X86_64_DECLARE_EVENT_HANDLER(NAME) \
  friend void anima_x86_64_int_ ## NAME ## _handler (int_frame_t *); \
  INLINE static void NAME ## _handler (int_frame_t *);

  __ANIMA_X86_64_DECLARE_EVENT_HANDLER (ignore)

  __ANIMA_X86_64_DECLARE_EVENT_HANDLER (apic_timer0)
  __ANIMA_X86_64_DECLARE_EVENT_HANDLER (apic_timer)

#undef __ANIMA_X86_64_DECLARE_EVENT_HANDLER

public:
  static bool has_irq_p (u32_t irq);
  static bool route_irq (u32_t irq, id_t id, u8_t vector,
                         ioapic_t::trig_mode_t tm,
                         ioapic_t::polarity_t polarity);
  static bool unroute_irq (u32_t irq);

public:
  static void *map_temp (u64_t paddr);
  static void unmap_temp ();

public:
  INLINE
  static void
  relax ()
  {
    asm volatile (
      "rep; nop"
    );
  }

  INLINE
  static void
  wait_for_int ()
  {
    if (use_hlt_p)
      ::x86_64::halt ();
    else
      {
        while (1)
          relax ();
      }
  }

  INLINE
  static void
  enable_ints ()
  {
    ::x86_64::enable_ints ();
  }

  INLINE
  static void
  disable_ints ()
  {
    ::x86_64::disable_ints ();
  }
};

END_NAMESPACE2

#endif /* HEADER_ANIMA_X86_64_CPU_HH */

